package org.twf.aicodehelper.config;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.twf.aicodehelper.config.ai.tools.SearchTool;
import org.twf.aicodehelper.service.IAiCodeHelperMemoryService;
import org.twf.aicodehelper.service.IAiCodeHelperService;

import java.util.List;

@Configuration
public class AiCodeHelperServiceFactory {

//    使用构造器自行构造对象
//    ChatModel qwenModel = QwenChatModel.builder()
//            .apiKey("You API key here")
//            .modelName("qwen-max")
//            .enableSearch(true)
//            .temperature(0.7)
//            .maxTokens(4096)
//            .stops(List.of("Hello"))
//            .build();


    @Resource
    @Qualifier("qwenChatModel")
    private ChatModel qwenChatModel;

    @Resource
    @Qualifier("myQwenChatModel") // 或者使用 "qwenChatModel"
    private ChatModel myQwenChatModel;

    /**
     * 流式对话模型
     */
    @Resource
    private StreamingChatModel qwenStreamingChatModel;


    // 注入 PersistentChatMemoryStore bean
    @Resource
    private MySqlChatMemoryStore mySqlChatMemoryStore;


    /**
     * 利用反射机制
     * 创建AiCodeHelperService代理对象
     *
     * @return
     */
    @Bean("aiCodeHelperService")
    public IAiCodeHelperService createAiCodeHelperService() {
        //接口中若使用了@@MemoryId注解 就必须有 ChatMemoryProvider
        return AiServices.create(IAiCodeHelperService.class, qwenChatModel);
    }


    /**
     * 创建使用会话记忆的代理对象
     *
     * @return
     */
    @Bean("aiCodeHelperServiceWithMemory")
    public IAiCodeHelperMemoryService createMemoryService() {
        // 会话记忆
        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
        IAiCodeHelperMemoryService aiCodeHelperService = AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                .chatMemory(chatMemory)
//                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder().maxMessages(10).id(memoryId).build())
                .build();
        return aiCodeHelperService;
    }

    /**
     * 创建使用数据库存储会话记忆的代理对象
     *
     * @return
     */
    @Bean("aiCodeHelperServiceWithDatabaseMemory")
    public IAiCodeHelperMemoryService createDatabaseMemoryService() {
        // 使用持久化存储的会话记忆能力
        return AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                // 会话记忆提供者，
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
                        .maxMessages(10)
                        .chatMemoryStore(mySqlChatMemoryStore)
                        .id(memoryId) //注意通过id返回不同的会话记忆实例
                        .build())
                .build();
    }

    /*------------------------极简版RAG---------------------------*/

    /**
     * 创建使用包含RAG检索增强能力的代理对象
     * 极简版
     *
     * @return
     */
    @Bean("RAGService")
    public IAiCodeHelperMemoryService createRAGService() {
        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
        // 1. 加载文档
        List<Document> documents = FileSystemDocumentLoader.loadDocuments("src/main/resources/docs");
        // 2. 使用内置的 EmbeddingModel 转换文本为向量，然后存储到自动注入的内存 embeddingStore 中
        EmbeddingStoreIngestor.ingest(documents, embeddingStore);
        // 构造 AI Service
        return AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                // 指定RAG：从内存 embeddingStore 中检索匹配的文本片段
                .contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore))
                // 会话记忆提供者，
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
                        .maxMessages(10)
                        .chatMemoryStore(mySqlChatMemoryStore)
                        .id(memoryId) //注意通过id返回不同的会话记忆实例
                        .build())
                .build();
    }


    /*-----------------------------标准版RAG-----------------------------*/

    @Resource
    private ContentRetriever contentRetriever;

    /**
     * 创建使用包含RAG检索增强能力的代理对象
     * 标准版 Standard
     *
     * @return
     */
    @Bean("standardRAGService")
    public IAiCodeHelperMemoryService createStandardRAGService() {
        // 构造 AI Service
        return AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                // 指定RAG
                .contentRetriever(contentRetriever)
                // 会话记忆提供者，
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
                        .maxMessages(10)
                        .chatMemoryStore(mySqlChatMemoryStore)
                        .id(memoryId) //注意通过id返回不同的会话记忆实例
                        .build())
                .build();
    }


    /**
     * 创建使用包含Bing搜索工具的代理对象
     *
     * @return
     */
    @Bean("bingService")
    public IAiCodeHelperMemoryService createBingService() {
        // 构造 AI Service
        return AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                // 添加Bing搜索工具
                .tools(new SearchTool())
                // 会话记忆提供者，
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
                        .maxMessages(10)
                        .chatMemoryStore(mySqlChatMemoryStore)
                        .id(memoryId) //注意通过id返回不同的会话记忆实例
                        .build())
                .build();
    }

    /*-------------------------------MCP工具调用------------------------------------*/


    @Resource
    private McpToolProvider mcpToolProvider;

    @Bean("mcpService")
    public IAiCodeHelperMemoryService createMcpService() {
        // 构造 AI Service
        return AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                // 指定RAG
                .contentRetriever(contentRetriever)
                // 会话记忆提供者，
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
                        .maxMessages(10)
                        .chatMemoryStore(mySqlChatMemoryStore)
                        .id(memoryId) //注意通过id返回不同的会话记忆实例
                        .build())
                .toolProvider(mcpToolProvider)
                .build();
    }

    /*---------------------------可观测性--------------------------------*/

    @Bean("logService")
    public IAiCodeHelperService createLogService() {
        //接口中若使用了@@MemoryId注解 就必须有 ChatMemoryProvider
        return AiServices.create(IAiCodeHelperService.class, myQwenChatModel);
    }


    /*-------------------------------流式输出---------------------------------------*/

    @Bean("webAndStreamService")
    public IAiCodeHelperMemoryService createWebAndStreamService() {
        // 构造 AI Service
        return AiServices.builder(IAiCodeHelperMemoryService.class)
                .chatModel(qwenChatModel)
                .streamingChatModel(qwenStreamingChatModel)
                // 指定RAG
                .contentRetriever(contentRetriever)
                // 会话记忆提供者，
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder()
                        .maxMessages(10)
                        .chatMemoryStore(mySqlChatMemoryStore)
                        .id(memoryId) //注意通过id返回不同的会话记忆实例
                        .build())
                .toolProvider(mcpToolProvider)
                .build();
    }


}